package q2d.common;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;

import q2d.client.renderers.GFactory;
import q2d.common.models.Map;
import q2d.common.models.TileTypes;
import q2d.common.models.units.Bullet;
import q2d.common.models.units.Player;

public class CollisionDetector {

	
	
	
	/**
	 * sprawdza kolizje miedzy graczem a mapa
	 * @param mapa -mapa  
	 * @param player - gracz
	 */
	public boolean isPlayerMapCollision(Map map,double playerX,double playerY){
		int row,column;
		int tileType;
		
		
		// wymiary srodka gracza
		double playerCenterX = playerX+Player.BOUND_RADIUS;
		double playerCenterY = playerY+Player.BOUND_RADIUS;

		// sprawdzamy srodek gracza
		row = countRow(playerCenterY);
		column = countColumn(playerCenterX);
	
		tileType = map.getTile(column,row);
		if(tileType>=TileTypes.WALL_1) return true;
		
		//sprawdzamy pozostale 8 punktow
		
		if(checkRow(row, column, countRow(playerCenterY + Player.COLLISION_RADIUS), map))return true;
		if(checkRow(row, column, countRow(playerCenterY - Player.COLLISION_RADIUS), map))return true;
		if(checkColumn(row, column,countColumn(playerCenterX - Player.COLLISION_RADIUS) , map)) return true;
		if(checkColumn(row, column,countColumn(playerCenterX + Player.COLLISION_RADIUS) , map)) return true;
		double cosinusR = Math.cos(Math.PI/4)*Player.COLLISION_RADIUS ;
		
		if(check(row, column, countRow(playerCenterY +cosinusR ), countColumn(playerCenterX+ cosinusR), map))return true;
		if(check(row, column, countRow(playerCenterY -cosinusR ), countColumn(playerCenterX+ cosinusR), map))return true;
		if(check(row, column, countRow(playerCenterY +cosinusR ), countColumn(playerCenterX- cosinusR), map))return true;
		if(check(row, column, countRow(playerCenterY -cosinusR ), countColumn(playerCenterX- cosinusR), map))return true;
		// punkty po przekatnych
		

		return false;
	}
	/**
	 * Sprawdza czy gracz moze zderzyc sie ze sciana w sasiednim wierszu
	 * @param row - wiersz polozenia srodka gracza
	 * @param column - kolumna polozenia srodka gracza
	 * @param tempRow - wiersz do sprawdzenia
	 * @param map - mapa 
	 * @return
	 */
	private boolean checkRow(int row,int column,int tempRow,Map map){
		if(tempRow<0) return false;
		int tileType;
	
		if(tempRow!= row){ 
			tileType  = map.getTile(column,tempRow );
			if(tileType>=TileTypes.WALL_1) return true;
		}
	
		return false;
	}

	/**
	 * Sprawdza czy gracz moze zderzyc sie ze sciana w sasiednim wierszu
	 * @param row - wiersz polozenia srodka gracza
	 * @param column - kolumna polozenia srodka gracza
	 * @param tempColumn - kolumna do sprawdzenia
	 * @param map - mapa do sprawdzenia
	 * @return
	 */
	 
	private boolean checkColumn(int row,int column,int tempColumn,Map map){
		if(tempColumn<0) return false; 
		int tileType;
		if(tempColumn!= column){ 
			tileType  = map.getTile( tempColumn,row);
			if(tileType>=TileTypes.WALL_1) return true;
		}
		return false;
	}
	/**
	 * Sprawdza czy gracz moze zderzyc sie ze siciana w sasiednim wierszu i kolumnie
	 * @param row - wiersz polozenia srodka gracza
	 * @param column - kolumna polozenia srodka gracza
	 * @param tempRow - wiersz do sprawdzenia
	 * @param tempColumn - kolumna do sprawdzenia
	 * @param map
	 * @return
	 */
	private boolean check(int row,int column,int tempRow,int tempColumn, Map map){
		if(tempColumn<0 || tempRow<0) return false; 
		int tileType;
		
		if((tempColumn!= column) || (tempRow != row)){ 
			
			tileType  = map.getTile( tempColumn,tempRow);
			if(tileType>=TileTypes.WALL_1) return true;
		}
		return false;
	}
	
	

	
	
	
	public float isBulletMapCollision(Map map ,double x,double y, double dx, double dy){
		return isVectorMapColision(map, x, y, dx, dy);
	}
	
	private float isVectorMapColision(Map map, double x,double y, double dx, double dy){
		double s = dx/dy;
		x += Bullet.TYPE_1_RADIUS; // srodek kulki
		y+= Bullet.TYPE_1_RADIUS;
		int rowBegin = countRow(y);
		int columnBegin = countColumn(x);
		
		////System.out.println("row" +	rowBegin + "column" +columnBegin);

		//punkt poczatkowy wektora
		if(map.getTile(columnBegin, rowBegin) >= TileTypes.WALL_1) return 0;
		
		//punkt koncowy wektora
		int rowEnd = countRow(y+dy);
		int columnEnd = countColumn(x+dx);
		////System.out.println("row" +	rowEnd + "column" + columnEnd);

		if(rowEnd == rowBegin && columnBegin == columnEnd) return -1;
		if(map.getTile(columnEnd, rowEnd) >= TileTypes.WALL_1) return 1;

		double deltaY;
		double deltaX;
		
		if(dy>0)deltaY = (rowBegin+1)*GFactory.TILE_HEIGHT -  y +Bullet.TYPE_1_RADIUS;
		else deltaY =(rowBegin-1)*GFactory.TILE_HEIGHT -  y +Bullet.TYPE_1_RADIUS;
		
		if (dx>0) deltaX = (columnBegin+1)*GFactory.TILE_WIDTH -  x+Bullet.TYPE_1_RADIUS;
		else deltaX = (columnBegin-1)*GFactory.TILE_WIDTH -  x+Bullet.TYPE_1_RADIUS;
		
		////System.out.println(deltaY + "y");
		////System.out.println(deltaX + "x");
		
	
		double tempDx;
		while(Math.abs(deltaY) < Math.abs(dy)){
			tempDx = deltaY*s + x;
			////System.out.println("row" +	countRow(deltaY+y) + "column(count)" +countColumn(tempDx));
			if(dy>0){
				if(checkColumn(countRow(deltaY+y), columnBegin, countColumn(tempDx), map))return (float) (tempDx/x);						
				deltaY += GFactory.TILE_HEIGHT;
				}
			else{
				if(checkColumn(countRow(deltaY+y)-1, columnBegin, countColumn(tempDx), map))return (float) (tempDx/x);
				deltaY -= GFactory.TILE_HEIGHT;
				}			 
		}
		double tempDy;
		while(Math.abs(deltaX) < Math.abs(dx)){
			tempDy = deltaX/s + y;
			////System.out.println("row(count)" +	countRow(tempDy) + "column" +countColumn(deltaX + x));								
			if (dx>0){
				if(checkRow(rowBegin, countColumn(deltaX + x), countRow(tempDy), map))return (float)(tempDy/y);
				deltaX += GFactory.TILE_WIDTH;
			}
			else{
				if(checkRow(rowBegin, countColumn(deltaX + x) -1, countRow(tempDy), map))return (float)(tempDy/y);
				deltaX-= GFactory.TILE_WIDTH;
			}
					
		}
		return -1;
	}
	
	public boolean isBulletPlayerCollision( double xBullet,double yBullet, double dx, double dy,double xPlayer,double yPlayer){
		//sprawdzenie poczatku
		xPlayer +=Player.BOUND_RADIUS;
		yPlayer += Player.BOUND_RADIUS;
		double d = Math.sqrt(Math.pow(xBullet-xPlayer,2) + Math.pow(yBullet - yPlayer, 2));
		if(d<= Player.COLLISION_RADIUS  ) return true; 
		// sprawdzenie konca wektora
		d = Math.sqrt(Math.pow(xBullet+dx-xPlayer,2) + Math.pow(yBullet+dy - yPlayer, 2));
		if(d<= Player.COLLISION_RADIUS  ) return true;
		
		//a1 = (y2-y1)/(x2-x1) wspolczynniki wektora
		
		double a1 = 0;
		if(dx!=0)a1 = dy/dx;
		////System.out.println(a1 + "a1");
		// b1 = (y1x2 - y2x1)/(x2-x1)
		double b1 = 0;
		if(dx!=0) b1 = (yBullet*(xBullet+dx) - (yBullet+dy)*xBullet)/dx;
		////System.out.println(b1 + "b1");
		// a2 = -1/a1 wspolczynnnik prostej prostopadlej
		double a2 =0;
		if(a1!=0) a2= (-1)/a1;
		// b2 = y3 -a2x3
		double b2 = yPlayer - a2*xPlayer;
		//x4 = (b1-b2)/(a2-a1) przeciecie prostych
		
		double x4 =xBullet;
		if((a2-a1)!=0) x4 =(b1 -b2)/(a2-a1);
		////System.out.println(x4 + "x4");
		//y4 =(b1a2 - a1b2)/(a2-a1)
		double y4 =b2;
		if((a2-a1)!=0)y4= (b1*a2 - a1*b2)/(a2-a1);
		////System.out.println(y4 + "y4");
		double distance = Math.sqrt(Math.pow(x4-xPlayer,2) + Math.pow(y4 - yPlayer, 2));
		if(distance<= Player.COLLISION_RADIUS && (x4>= Math.min(xBullet, xBullet+dx)&& x4 <= Math.max(xBullet, xBullet+dx))&& (y4>= Math.min(yBullet, yBullet+dy)&& y4 <= Math.max(yBullet, yBullet+dy)) ) return true; // i sprawdz przedzialy
		//System.out.println(distance + "distance");
	
		return false;
	}


	/**
	 * Sprawdza kolizje gracza z flaga. 
	 * @param flag_x
	 * @param flag_y
	 * @param player_x
	 * @param player_y
	 * @return
	 */
	public boolean isPlayerFlagCollision(int flag_x, int flag_y, int player_x, int player_y){
		return Math.sqrt(Math.pow(flag_x-player_x, 2)+Math.pow(flag_y-player_y, 2)) < Player.BOUND_RADIUS	;
	}


	/**
	 * Sprawdza kolizje flagi z flaga. 
	 * @param flag1_x
	 * @param flag1_y
	 * @param flag2_x
	 * @param flag2_x
	 * @return
	 */
	public boolean isFlagFlagCollision(int flag1_x, int flag1_y, int flag2_x, int flag2_y){
		return Math.sqrt(Math.pow(flag1_x-flag2_x, 2)+Math.pow(flag1_y-flag2_y, 2)) < Player.BOUND_RADIUS	;
	}


	/**
	 * oblicza numer wiersza w mapie na podstawie polozenia
	 * @param y
	 * @return
	 */
	private int countRow(double y){
		if (y>0) return (int)Math.floor(y/GFactory.TILE_HEIGHT);
		return 0;
	}
	/**
	 *oblicza numer kolumny w mapie na podstawie polozenia
	 * @param x
	 * @return
	 */
	
	private int countColumn(double x){
		if (x>0) return (int)Math.floor(x/GFactory.TILE_WIDTH);
		return 0;
	}
}
